package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.commom.constants.message.NewsAutoScanConstants;
import com.heima.commom.constants.message.NewsUpOrDownConstants;
import com.heima.commom.constants.message.PublishArticleConstants;
import com.heima.common.exception.CustException;
import com.heima.model.common.constants.wemedia.WmMaterialConstant;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.NewsAuthDTO;
import com.heima.model.wemedia.dtos.WmNewsDTO;
import com.heima.model.wemedia.dtos.WmNewsDownOrUpDTO;
import com.heima.model.wemedia.dtos.WmNewsPageReqDTO;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vos.WmNewsVO;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmMaterialService;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Value("${file.oss.web-site}")
    private String webSite;
    /**
     * 分页自媒体带条件列表
     * @param dto
     * @return
     */

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    WmNewsMaterialMapper wmNewsMaterialMapper;

    @Autowired
    WmMaterialMapper wmMaterialMapper;

    @Autowired
    WmMaterialService wmMaterialService;
    @Override
    public PageResponseResult list(WmNewsPageReqDTO dto) {
        if(dto == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }

        //获取当前登录用户 如果为空 先登录
        WmUser user = WmThreadLocalUtils.getUser();
        if(user == null){
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }

        //构建查询条件
        LambdaQueryWrapper<WmNews> wrapper = Wrappers.<WmNews>lambdaQuery();
        //1. 如果有文章标题 按照文章标题模糊查询
        wrapper.like(StringUtils.isNotBlank(dto.getKeyword()), WmNews::getTitle, dto.getKeyword());
        //如果有频道信息 按照频道id去查询
        wrapper.eq(dto.getChannelId() != null, WmNews::getChannelId, dto.getChannelId());
        //如果有文章状态 ,按照状态信息查询
        wrapper.eq(dto.getStatus() != null, WmNews::getStatus,dto.getStatus());
        //如果开始时间 结束时间不为空按照时间范围i查询
        wrapper.between(dto.getBeginPubDate()!=null&&dto.getEndPubDate()!=null,
                WmNews::getCreatedTime,
                dto.getBeginPubDate(),
                dto.getEndPubDate());
        //按照登录用户id去查询
        wrapper.eq(WmNews::getUserId,user.getId());
        //按照创建时间降序
        wrapper.orderByDesc(WmNews::getCreatedTime);

        Page<WmNews> page = new Page<>(dto.getPage(),dto.getSize());
        IPage<WmNews> wmNewsIPage = page(page,wrapper);

        PageResponseResult pageResponseResult = new PageResponseResult(dto.getPage(), dto.getSize(), wmNewsIPage.getTotal(), wmNewsIPage.getRecords());
        pageResponseResult.setHost(webSite);


        return pageResponseResult;
    }


    /**
     * 自媒体文章发布 修改 保存草稿
     * @param dto
     * @return
     */
    @Override
    public ResponseResult submit(WmNewsDTO dto) {
        /*检查参数 封装WmNews对象
            1.1 检查内容 标题 是否登录
        *
        */
        if(dto == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }

        //检查内容标题 以及是否登录
        //获取当前登录用户
        WmUser user = WmThreadLocalUtils.getUser();
        WmNews wmNews = extracted(dto,user);

        //如果是草稿的话 直接保存
        if(WmMaterialConstant.WM_NEWS_DRAFT_STATUS.equals(dto.getStatus())){
            save(wmNews);
            return ResponseResult.okResult();
        }

        //保存或修改文章
        //判断id是否为空 为空则是保存 不为空则是 修改
        if(dto.getId() == null){
            //设置用户信息 保存
            wmNews.setUserId(WmThreadLocalUtils.getUser().getId());
            wmNews.setCreatedTime(new Date());
            wmNews.setEnable(WmMaterialConstant.WM_NEWS_UP);
            wmNews.setPublishTime(new Date());
            wmNews.setSubmitedTime(new Date());
            save(wmNews);
        }else {
            //如果不为空则为修改 需要先删除文章和素材的关联关系
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,dto.getId()));
            updateById(wmNews);
        }
        //保存文章内容中的素材关联
            //获取文章中的内容素材如果为空 不用保存素材关联
        String content = dto.getContent();
        List<String> list = parseContentImages(content);
        if(CollectionUtils.isNotEmpty(list)){
            log.info("保存文章内容中的素材 当前用户id: {}",user.getId());
            saveRelations(wmNews,list,WmMaterialConstant.WM_CONTENT_REFERENCE);
        }

        //保存封面素材的关联关系  如果选择不是无图模式的话
        if(!WmMaterialConstant.WM_NEWS_NONE_IMAGE.equals(dto.getType()) ){
            log.info("保存封面素材 当前登录用户id: {}",user.getId());
            saveFrontCoverMaterial( wmNews, dto, list,user.getId());
        }
        rabbitTemplate.convertAndSend(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_QUEUE,wmNews.getId());
        log.info("成功发送 待审核消息 ==> 队列:{}, 文章id:{}",NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_QUEUE,wmNews.getId());
        return ResponseResult.okResult();
    }

    /**
     *  保存文章内容素材的关联关系
     * @param wmNews 文章对象
     * @param stringList   文章中内容素材的路径字符串
     */
    private void saveRelations(WmNews wmNews,List<String> stringList ,Short type ){
        //根据文章内容中的素材路径 在数据库中查找对应的素材
        List<Integer> list = wmMaterialMapper.selectRelationsIds(stringList, WmThreadLocalUtils.getUser().getId());
        if(stringList.size() != list.size() || CollectionUtils.isEmpty(list)){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"相关素材缺失");
        }
        //保存关联表 内容中的素材
        wmNewsMaterialMapper.saveRelations(list,wmNews.getId(),type);
    }

    /**
     * 保存 文章封面素材的关联
     * @param wmNews 文章对象
     * @param dto    前端请求的参数
     * @param stringList 文章内容中的素材
     * @param userId    当前登录的用户id
     */
    private void saveFrontCoverMaterial(WmNews wmNews,WmNewsDTO dto,List<String> stringList,Integer userId){
        //封面中的图
        List<String> images = dto.getImages();
        //如果用户选择自动的话
        if(WmMaterialConstant.WM_NEWS_TYPE_AUTO.equals(dto.getType())){
            //文章内容中的素材
            int size = stringList.size();
            if(size > 0 && size <= 2){ //n内容中如果图片为一张的话 就封面给一张图
                images = stringList.stream().limit(1).collect(Collectors.toList());
                wmNews.setType(WmMaterialConstant.WM_NEWS_SINGLE_IMAGE); //单图文章
            }else if(size > 2){
                images = stringList.stream().limit(3).collect(Collectors.toList());
                wmNews.setType(WmMaterialConstant.WM_NEWS_MANY_IMAGE); //3图文章
            }else {
                wmNews.setType(WmMaterialConstant.WM_NEWS_NONE_IMAGE);//无图
            }
            if(images != null && images.size() > 0){
                //将图片集合 转为字符串
                wmNews.setImages(imagesToStr(images));
            }
            updateById(wmNews);
            //保存图片列表和素材的关系
            if(images != null && images.size() > 0){
              images = images.stream()
                      .map(x -> x.replace(webSite,"").replace(" ",""))
                      .collect(Collectors.toList());
              saveRelations(wmNews,images,WmMaterialConstant.WM_IMAGE_REFERENCE);
            }
        }
    }

    /**
     * 检查内容 标题 是否登录
     * @param dto 请求参数
     * @return 返回wmNews对象
     */
    private WmNews extracted(WmNewsDTO dto ,WmUser user) {
        //未登录则提示登录
        if(user == null){
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        //检查标题是否为空
        if (StringUtils.isBlank(dto.getTitle())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"标题不能为空");
        }
        //文章内容不能为空
        if (StringUtils.isBlank(dto.getContent())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"文章内容不能为空");
        }
        //拷贝属性到wmNews对象中
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto,wmNews);
        if(dto.getType().equals(WmMaterialConstant.WM_NEWS_TYPE_AUTO)){
            wmNews.setType(null);
        }
        //前端响应数据中的images 是list  wmNews中对应的是string 需要转换一下
        List<String> images = dto.getImages();
        String toStr = imagesToStr(images);
        wmNews.setImages(toStr);

        return wmNews;
    }

    //将集合中的路径 去头 转为string
    private String imagesToStr(List<String> images){
        String image = images.stream()
                .map(url -> url.replace(webSite, ""))
                .collect(Collectors.joining(","));
        return image;
    }

    //从文章内容中抽取所有的素材路径
    private List<String> parseContentImages(String content) {
        List<Map> maps = JSON.parseArray(content, Map.class);
        List<String> list = maps.stream()
                .filter(map -> map.get("type").equals(WmMaterialConstant.WM_NEWS_TYPE_IMAGE))
                .map(x -> (String) x.get("value"))
                .map(url -> url.replace(webSite, "").replace(" ", ""))
                .distinct()
                .collect(Collectors.toList());
        return list;
    }

    /**
     * 根据id查询文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult findWmNewsById(Integer id) {
        if(id == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNews wmNews = getById(id);
        if(wmNews == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        ResponseResult responseResult =  ResponseResult.okResult(wmNews);
        responseResult.setHost(webSite);
        return responseResult;
    }


    /**
     * 根据id删除文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult delNewsById(Integer id) {
        if(id == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        //查询文章状态 如果为上架(9)的话 不能删除
        WmNews wmNews = getById(id);
        if(wmNews == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        String labels = wmNews.getLabels();
        if(WmMaterialConstant.WM_NEWS_PUBLISH_STATUS.equals(wmNews.getStatus()) && WmMaterialConstant.WM_NEWS_DOWN.equals(labels)){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW,"文章已发布且处于上架状态 不能删除");
        }
        //先删除关联的素材
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,id));
        //再删除相关文章
        removeById(id);
        return ResponseResult.okResult();
    }

    @Autowired
    WmNewsMapper wmNewsMapper;

    //查询文章列表
    @Override
    public ResponseResult findList(NewsAuthDTO dto) {
        //1.检查参数
        dto.checkParam();
        //记录当前页
        int currentPage = dto.getPage();
        //设置起始页
        dto.setPage((dto.getPage()-1)*dto.getSize());

        if(StringUtils.isNotBlank(dto.getTitle())){
            //  concat(%,?,%)
            dto.setTitle("%"+dto.getTitle()+"%");
        }

        //2.分页查询
        List<WmNewsVO> wmNewsVoList = wmNewsMapper.findListAndPage(dto);
        //统计多少条数据
        long count = wmNewsMapper.findListCount(dto);

        //3.结果返回
        ResponseResult result = new PageResponseResult(currentPage, dto.getSize(), count, wmNewsVoList);
        result.setHost(webSite);
        return result;
    }

    @Autowired
    WmUserMapper wmUserMapper;
    /**
     * 根据id 查询文章详情
     * @param id 文章id
     * @return
     */
    @Override
    public ResponseResult findWmNewsVo(Integer id) {
        if(id == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        //查询文章信息
        WmNews wmNews = getById(id);
        if(wmNews == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //查询作者
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if(wmUser == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        WmNewsVO wmNewsVO = new WmNewsVO();
        BeanUtils.copyProperties(wmNews,wmNewsVO);
        wmNewsVO.setAuthorName(wmUser.getName());

        ResponseResult responseResult = ResponseResult.okResult(wmNewsVO);
        responseResult.setHost(webSite);

        return responseResult;
    }

    /**
     * 人工审核文章
     * @param dto 文章信息
     * @param status 修改的状态 通过为4 驳回为2
     * @return
     */
    @Override
    public ResponseResult updaStatus(NewsAuthDTO dto, Short status) {
        if(dto == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        //查询文章状态
        WmNews wmNews = wmNewsMapper.selectById(dto.getId());
        if(WmMaterialConstant.WM_NEWS_PUBLISH_STATUS.equals(wmNews.getStatus())){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW,"文章为发布状态不可操作");
        }
        wmNews.setStatus(status);
        if(dto.getMsg() != null){
            wmNews.setImages(dto.getMsg());
        }
        updateById(wmNews);

        //通知定时发布任务
        //发布时间
        long publishTime = wmNews.getPublishTime().getTime();
        //当前时间
        long nowTime = new Date().getTime();
        //设置延迟发布任务时间
        long remainTime = publishTime - nowTime ;
        //发布文章消息
        rabbitTemplate.convertAndSend(PublishArticleConstants.DELAY_DIRECT_EXCHANGE //设置交换机
                ,PublishArticleConstants.PUBLISH_ARTICLE_ROUTE_KEY                  // 设置路由key
                ,wmNews.getId()     //发送的消息
                ,(message -> {      // 设置延时时间 如果 发布时间小于当前时间 设为0
                    message.getMessageProperties().setHeader("x-delay",remainTime <= 0 ? 0 : remainTime);
                    return message;
                }));
        log.info("发布文章的通知消息 成功发送 文章的id为:{}",wmNews.getId());
        return ResponseResult.okResult();
    }


    /**
     * 文章下架
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDownOrUpDTO dto) {
        if(dto == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        Integer id = dto.getId();
        WmNews wmNews = getById(id);
        if(wmNews == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if(!WmMaterialConstant.WM_NEWS_PUBLISH_STATUS.equals(wmNews.getStatus())){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"当前文章不是发布状态 上下架操作失败");
        }

        update(Wrappers.<WmNews>lambdaUpdate()
                .eq(WmNews::getId,id)
                .set(WmNews::getEnable,dto.getEnable()));

        //发送消息
        if(wmNews.getArticleId() != null){
            if(dto.getEnable().equals(WmMaterialConstant.WM_NEWS_UP.intValue())){
                // 上架消息
                rabbitTemplate.convertAndSend(NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE,
                        NewsUpOrDownConstants.NEWS_UP_ROUTE_KEY,wmNews.getArticleId());
            }else {
                // 下架消息
                rabbitTemplate.convertAndSend(NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE,
                        NewsUpOrDownConstants.NEWS_DOWN_ROUTE_KEY,wmNews.getArticleId());
            }
        }

        return ResponseResult.okResult();
    }
}
